using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Admin.NET.Application.Entity;
using Admin.NET.Application.Entity.Business;
using Admin.NET.Application.Excel;
using Admin.NET.Core;
using Furion;
using Furion.DependencyInjection;
using Furion.DynamicApiController;
using Furion.FriendlyException;
using Furion.TaskQueue;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.DependencyInjection;
using SqlSugar;

namespace Admin.NET.Application.Service.Electricity;

/// <summary>
/// 电费退补服务
/// </summary>
[ApiDescriptionSettings(new string[] { "业务应用" }, Order = 100)]
public class RetrieveService : IDynamicApiController, ITransient, IPrivateDependency
{
	private readonly SqlSugarRepository<PowerRateRetrieveRecord> _rep;

	private readonly UserManager _userManager;

	public RetrieveService(SqlSugarRepository<PowerRateRetrieveRecord> res, UserManager userManager)
	{
		_rep = res;
		_userManager = userManager;
	}

	/// <summary>
	/// 获取列表
	/// </summary>
	/// <param name="input"></param>
	/// <returns></returns>
	[HttpGet("/retrieve/page")]
	public async Task<dynamic> Page([FromQuery] RetrieveInput input)
	{
		ISugarQueryable<PowerRateRetrieveRecord> query = _rep.Context.Queryable<PowerRateRetrieveRecord>().WhereIF(!string.IsNullOrWhiteSpace(input.Name), (PowerRateRetrieveRecord u) => u.Name.Contains(input.Name.Trim())).WhereIF(!string.IsNullOrWhiteSpace(input.Cycle), (PowerRateRetrieveRecord u) => u.Cycle == input.Cycle.Trim())
			.WhereIF(!string.IsNullOrWhiteSpace(input.Sn), (PowerRateRetrieveRecord u) => u.Sn.Contains(input.Sn.Trim()))
			.WhereIF(input.IsTakeEffect.HasValue, (PowerRateRetrieveRecord u) => (int?)u.IsTakeEffect == input.IsTakeEffect)
			.WhereIF(input.BuildingId.HasValue, (PowerRateRetrieveRecord u) => u.BuildingId == input.BuildingId)
			.WhereIF(input.RoomId.HasValue, (PowerRateRetrieveRecord u) => (long?)u.RoomId == input.RoomId);
		query = query.OrderBuilder(input);
		return await query.ToPagedListAsync(input.Page, input.PageSize);
	}

	[HttpPost("/retrieve/createretrieve")]
	public async Task CreateRetrieve([FromBody] CreateRetrieveInput input)
	{
		if (input.RetrieveMeters.Count == 0)
		{
			throw Oops.Bah("请选择退补的电表");
		}
		DateTime.Now.ToString("yyyy-MM");
		List<string> sns = input.RetrieveMeters.Select((RetrieveMeter s) => s.Sn).ToList();
		if (await (from w in _rep.Context.Queryable<PowerRateRetrieveRecord>()
			where w.Cycle == input.Cycle && sns.Contains(w.Sn)
			select w).CountAsync() > 0)
		{
			throw Oops.Bah("同周期内存在已退补过的电表，请刷新后重新选择");
		}
		List<PowerRateRetrieveRecord> data = input.RetrieveMeters.Select((RetrieveMeter s) => new PowerRateRetrieveRecord
		{
			Cycle = input.Cycle,
			FlatPrice = input.FlatPrice,
			IsTakeEffect = 2,
			Name = s.Name,
			PeakPrice = input.PeakPrice,
			Price = input.Price,
			SharpPrice = input.SharpPrice,
			Sn = s.Sn,
			Type = input.Type,
			ValleyPrice = input.ValleyPrice,
			RoomId = s.RoomId,
			RoomName = s.RoomName,
			BuildingId = s.BuildingId,
			BuildingName = s.BuildingName
		}).ToList();
		_rep.InsertRange(data);
		await CalRetrieveNow(input.Cycle);
	}

	/// <summary>
	/// 立即计算
	/// </summary>
	/// <param name="cycle"></param>
	/// <returns></returns>
	[HttpPost("/retrieve/calretrievenow")]
	public async Task CalRetrieveNow([FromQuery] string cycle)
	{
		long tenantId = _userManager.TenantId;
		await TaskQueued.EnqueueAsync(async delegate(IServiceProvider provider, CancellationToken token)
		{
			using IServiceScope serviceScope = provider.CreateScope();
			SqlSugarRepository<PowerRateRetrieveRecord> retrievedb = serviceScope.ServiceProvider.GetService<SqlSugarRepository<PowerRateRetrieveRecord>>();
			SqlSugarRepository<PowerConsumptionRecord> recorddb = serviceScope.ServiceProvider.GetService<SqlSugarRepository<PowerConsumptionRecord>>();
			await CalRetrieve(retrievedb, recorddb, cycle, tenantId);
		});
	}

	private async Task CalRetrieve(SqlSugarRepository<PowerRateRetrieveRecord> retrievedb, SqlSugarRepository<PowerConsumptionRecord> recorddb, string cycle, long tenantId)
	{
		List<PowerRateRetrieveRecord> needcaldata = await (from w in retrievedb.AsQueryable()
			where w.TenantId == (long?)tenantId && w.Cycle == cycle && w.IsTakeEffect == 2
			select w).ToListAsync();
		if (needcaldata.Count <= 0)
		{
			return;
		}
		List<string> sns = needcaldata.Select((PowerRateRetrieveRecord s) => s.Sn).ToList();
		var datas = (from w in recorddb.AsQueryable()
			where w.CreateTime.Value.ToString("yyyy-MM") == cycle && sns.Contains(w.Sn)
			group w by w.Sn into w
			select new
			{
				total = SqlFunc.AggregateSum(w.CombinedActiveTotalElectricEnergyDifference),
				sharpTotal = SqlFunc.AggregateSum(w.CombinedActiveSharpElectricEnergyDifference),
				peakTotal = SqlFunc.AggregateSum(w.CombinedActivePeakElectricEnergyDifference),
				flatTotal = SqlFunc.AggregateSum(w.CombinedActiveFlatElectricEnergyDifference),
				valleyTotal = SqlFunc.AggregateSum(w.CombinedActiveValleyElectricEnergyDifference),
				amount = SqlFunc.AggregateSum(w.Amount),
				sn = w.Sn
			}).ToList();
		foreach (PowerRateRetrieveRecord item in needcaldata)
		{
			var tmp = datas.Where(w => w.sn == item.Sn).FirstOrDefault();
			item.IsTakeEffect = 0;
			if (tmp == null)
			{
				item.RefundPrice = default(decimal);
				item.RepairPrice = default(decimal);
				continue;
			}
			decimal total;
			decimal sharpTotal2;
			decimal? sharpPrice;
			if (item.Type == 0)
			{
				total = tmp.total * item.Price.Value;
			}
			else
			{
				if (item.Type != 1)
				{
					continue;
				}
				sharpTotal2 = tmp.sharpTotal;
				sharpPrice = item.SharpPrice;
				decimal? sharpTotal = (decimal?)sharpTotal2 * sharpPrice;
				sharpTotal2 = tmp.peakTotal;
				sharpPrice = item.PeakPrice;
				decimal? peakTotal = (decimal?)sharpTotal2 * sharpPrice;
				sharpTotal2 = tmp.flatTotal;
				sharpPrice = item.FlatPrice;
				decimal? flatTotal = (decimal?)sharpTotal2 * sharpPrice;
				sharpTotal2 = tmp.valleyTotal;
				sharpPrice = item.ValleyPrice;
				decimal? valleyTotal = (decimal?)sharpTotal2 * sharpPrice;
				total = sharpTotal.Value + peakTotal.Value + flatTotal.Value + valleyTotal.Value;
			}
			item.RefundPrice = default(decimal);
			item.RepairPrice = default(decimal);
			sharpTotal2 = total;
			sharpPrice = tmp.amount;
			decimal? difference = (decimal?)sharpTotal2 - sharpPrice;
			sharpPrice = difference;
			if ((sharpPrice.GetValueOrDefault() > default(decimal)) & sharpPrice.HasValue)
			{
				item.RepairPrice = difference;
				continue;
			}
			sharpPrice = difference;
			if ((sharpPrice.GetValueOrDefault() < default(decimal)) & sharpPrice.HasValue)
			{
				item.RefundPrice = difference;
				continue;
			}
			item.RefundPrice = default(decimal);
			item.RepairPrice = default(decimal);
		}
		await retrievedb.UpdateRangeAsync(needcaldata);
	}

	/// <summary>
	/// 获取可以退补的电表
	/// </summary>
	/// <param name="cycle"></param>
	/// <returns></returns>
	[HttpGet("/retrieve/getretrievemeters")]
	public async Task<dynamic> GetRetrieveMeters([FromQuery] string cycle)
	{
		List<Building> buildings = await _rep.Context.Queryable<Building>().ToListAsync();
		List<BuildingRoomOutput> root = (from w in buildings
			where w.Pid == 0
			select w into s
			select new BuildingRoomOutput
			{
				Id = s.Id,
				Pid = s.Pid,
				Label = s.Name,
				Disabled = false
			}).ToList();
		List<Room> rooms = await _rep.Context.Queryable<Room>().ToListAsync();
		List<string> sns = await (from w in _rep.AsQueryable()
			where w.Cycle == cycle
			select w.Sn).ToListAsync();
		List<ElectricMeter> meters = await (from s in _rep.Context.Queryable<ElectricMeter>()
			select new ElectricMeter
			{
				Id = s.Id,
				Sn = s.Sn,
				Name = s.Name,
				RoomId = s.RoomId
			}).ToListAsync();
		if (sns.Count > 0)
		{
			meters = meters.Where((ElectricMeter w) => !sns.Contains(w.Sn)).ToList();
		}
		List<long> ids = new List<long>();
		foreach (BuildingRoomOutput item in root)
		{
			MapChild3(item, buildings, ids, rooms, meters);
		}
		return root;
	}

	private void MapChild3(BuildingRoomOutput item, List<Building> list, List<long> ids, List<Room> rooms, List<ElectricMeter> electricMeters)
	{
		if (ids.Contains(item.Id))
		{
			return;
		}
		ids.Add(item.Id);
		item.Children = new List<BuildingRoomOutput>();
		List<BuildingRoomOutput> children = (from w in list
			where w.Pid == item.Id
			select new BuildingRoomOutput
			{
				Label = w.Name,
				Id = w.Id,
				Pid = w.Pid,
				Disabled = false,
				Children = new List<BuildingRoomOutput>()
			}).ToList();
		if (children.Count > 0)
		{
			item.Children.AddRange(children);
			foreach (BuildingRoomOutput child in children)
			{
				MapChild3(child, list, ids, rooms, electricMeters);
			}
		}
		if (rooms.Count <= 0)
		{
			return;
		}
		List<BuildingRoomOutput> tmprooms = (from w in rooms
			where w.BuildingId == item.Id
			select w into s
			select new BuildingRoomOutput
			{
				Id = s.Id,
				Disabled = false,
				Label = s.Name,
				Children = new List<BuildingRoomOutput>()
			}).ToList();
		if (tmprooms.Count <= 0)
		{
			return;
		}
		item.Children.AddRange(tmprooms);
		if (electricMeters.Count <= 0)
		{
			return;
		}
		foreach (BuildingRoomOutput room in tmprooms)
		{
			List<BuildingRoomOutput> tmpmeters = (from w in electricMeters
				where w.RoomId == room.Id
				select w into s2
				select new BuildingRoomOutput
				{
					Id = s2.Id,
					Label = s2.Name,
					Sn = s2.Sn,
					BuildingId = item.Id,
					RoomId = room.Id,
					Disabled = false,
					RoomName = room.Label,
					BuildingName = item.Label
				}).ToList();
			if (tmpmeters.Count > 0)
			{
				room.Children.AddRange(tmpmeters);
			}
		}
	}

	/// <summary>
	/// 删除所有未生效的退补记录
	/// </summary>
	/// <param name="ids"></param>
	/// <returns></returns>
	[HttpPost("/retrieve/deleteretrievemeters")]
	public async Task DeleteRetrieveMeters([FromBody] List<long> ids)
	{
		if (await (from w in _rep.AsQueryable()
			where ids.Contains(w.Id) && w.IsTakeEffect == 1
			select w).CountAsync() > 0)
		{
			throw Oops.Bah("存在已生效的退补记录，请刷新后重新选择");
		}
		await _rep.Context.Deleteable<PowerRateRetrieveRecord>().In(ids).ExecuteCommandAsync();
	}

	/// <summary>
	/// 立即生效
	/// </summary>
	/// <param name="ids"></param>
	/// <returns></returns>
	[HttpPost("/retrieve/effectiveimmediately")]
	public async Task EffectiveImmediately([FromBody] List<long> ids)
	{
		try
		{
			await _rep.Context.Ado.BeginTranAsync();
			if (await (from w in _rep.AsQueryable()
				where ids.Contains(w.Id) && w.IsTakeEffect != 0
				select w).CountAsync() > 0)
			{
				throw Oops.Bah("存在已生效或正在计算当中的退补记录，请刷新后重新选择");
			}
			foreach (PowerRateRetrieveRecord record in await (from w in _rep.AsQueryable()
				where ids.Contains(w.Id)
				select w).ToListAsync())
			{
				RetrieveRecord(record);
			}
			await (from w in _rep.Context.Updateable<PowerRateRetrieveRecord>().SetColumns((PowerRateRetrieveRecord s) => s.IsTakeEffect == 1)
				where ids.Contains(w.Id)
				select w).ExecuteCommandAsync();
			await _rep.Context.Ado.CommitTranAsync();
		}
		catch (Exception ex)
		{
			await _rep.Context.Ado.RollbackTranAsync();
			throw ex;
		}
	}

	private void RetrieveRecord(PowerRateRetrieveRecord record)
	{
		decimal? repairPrice = record.RepairPrice;
		if ((repairPrice.GetValueOrDefault() == default(decimal)) & repairPrice.HasValue)
		{
			repairPrice = record.RefundPrice;
			if ((repairPrice.GetValueOrDefault() == default(decimal)) & repairPrice.HasValue)
			{
				return;
			}
		}
		if (!record.RepairPrice.HasValue)
		{
			repairPrice = record.RefundPrice;
			if ((repairPrice.GetValueOrDefault() == default(decimal)) & repairPrice.HasValue)
			{
				return;
			}
		}
		repairPrice = record.RepairPrice;
		if (((repairPrice.GetValueOrDefault() == default(decimal)) & repairPrice.HasValue) && !record.RefundPrice.HasValue)
		{
			return;
		}
		Room room = (from w in _rep.Context.Queryable<Room>()
			where w.Id == record.RoomId
			select w).First();
		if (room == null)
		{
			return;
		}
		IUpdateable<Room> tmp = _rep.Context.Updateable<Room>();
		DateTime now = DateTime.Now;
		PaymentRecord paymentrecord = new PaymentRecord
		{
			RoomId = record.RoomId,
			BuildingId = room.BuildingId,
			NotifyTime = now,
			Body = "电费退补",
			PayType = PayTypeEnum.退补,
			PaymentName = "电费退补",
			BeforeAmount = room.Balance,
			OwnerId = room.OwnerId,
			Remark = "动态电费退补",
			CreateUserName = _userManager.RealName
		};
		if (record.RefundPrice.HasValue)
		{
			repairPrice = record.RefundPrice;
			if ((repairPrice.GetValueOrDefault() > default(decimal)) & repairPrice.HasValue)
			{
				tmp = tmp.SetColumns((Room s) => new Room
				{
					Balance = s.Balance + (decimal)record.RefundPrice
				});
				paymentrecord.IsPay = 1;
				paymentrecord.Amount = record.RefundPrice.Value;
				goto IL_04d5;
			}
		}
		if (record.RepairPrice.HasValue)
		{
			repairPrice = record.RepairPrice;
			if ((repairPrice.GetValueOrDefault() > default(decimal)) & repairPrice.HasValue)
			{
				tmp = tmp.SetColumns((Room s) => new Room
				{
					Balance = s.Balance - (decimal)record.RepairPrice
				});
				paymentrecord.IsPay = 4;
				paymentrecord.Amount = -Math.Abs(record.RepairPrice.Value);
			}
		}
		goto IL_04d5;
		IL_04d5:
		tmp.Where((Room s) => s.Id == record.RoomId).ExecuteCommand();
		_rep.Context.Insertable(paymentrecord).ExecuteCommand();
	}

	/// <summary>
	/// 获取正在计算中的周期
	/// </summary>
	/// <returns></returns>
	[HttpGet("/retrieve/getcycles")]
	public async Task<dynamic> GetCycles()
	{
		return await (from w in _rep.AsQueryable()
			where w.IsTakeEffect == 2
			select w into s
			select s.Cycle).Distinct().ToListAsync();
	}

	[HttpPost("/retrieve/export")]
	public async Task<IActionResult> ExportCorporate([FromQuery] RetrieveInput input)
	{
		string str = "电费退补-" + DateTime.Now.ToString("yyyy-MM-dd");
		List<string> column = new List<string>();
		List<string> title = new List<string>();
		ISugarQueryable<PowerRateRetrieveRecord> queryable = _rep.Context.Queryable<PowerRateRetrieveRecord>().WhereIF(!string.IsNullOrWhiteSpace(input.Name), (PowerRateRetrieveRecord u) => u.Name.Contains(input.Name.Trim())).WhereIF(!string.IsNullOrWhiteSpace(input.Cycle), (PowerRateRetrieveRecord u) => u.Cycle == input.Cycle.Trim())
			.WhereIF(!string.IsNullOrWhiteSpace(input.Sn), (PowerRateRetrieveRecord u) => u.Sn.Contains(input.Sn.Trim()))
			.WhereIF(input.IsTakeEffect.HasValue, (PowerRateRetrieveRecord u) => (int?)u.IsTakeEffect == input.IsTakeEffect)
			.WhereIF(input.BuildingId.HasValue, (PowerRateRetrieveRecord u) => u.BuildingId == input.BuildingId)
			.WhereIF(input.RoomId.HasValue, (PowerRateRetrieveRecord u) => (long?)u.RoomId == input.RoomId);
		RetrieveOutput[] list = await (from u in queryable
			orderby u.CreateTime descending
			select new RetrieveOutput
			{
				Id = u.Id,
				BuildingId = u.BuildingId,
				BuildingName = u.BuildingName,
				Cycle = u.Cycle,
				FlatPrice = u.FlatPrice,
				Sn = u.Sn,
				IsTakeEffect = u.IsTakeEffect,
				Name = u.Name,
				PeakPrice = u.PeakPrice,
				Price = u.Price,
				RefundPrice = u.RefundPrice,
				RepairPrice = u.RepairPrice,
				SharpPrice = u.SharpPrice,
				Type = u.Type,
				ValleyPrice = u.ValleyPrice,
				RoomId = u.RoomId,
				CreateTime = u.CreateTime,
				RoomName = u.RoomName
			}).ToArrayAsync();
		column.AddRange(new string[10] { "Cycle", "BuildingName", "RoomName", "Name", "Sn", "TypeName", "PriceName", "RefundPrice", "RepairPrice", "CreateTime" });
		title.AddRange(new string[10] { "周期", "区域", "房间", "电表名称", "电表号", "费率", "单价(元)", "退(元)", "补(元)", "提交时间" });
		MemoryStream memery = ExportToExcel.ExportToList(dic: new Dictionary<string, Dictionary<string, string>>(), listInfo: list, headerText: str, sheetName: str, properName: column.ToArray(), titleName: title.ToArray());
		App.HttpContext.Response.ContentType = "application/vnd.ms-excel";
		byte[] aa = memery.ToArray();
		return new FileStreamResult(new MemoryStream(aa), "application/octet-stream")
		{
			FileDownloadName = str + ".xlsx"
		};
	}
}
